package com.simafei.flow.core.api.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.simafei.flow.core.api.Api;
import com.simafei.flow.core.api.ApiAdapter;
import com.simafei.flow.core.api.ApiResolver;
import com.simafei.flow.core.api.SerializeType;
import com.simafei.flow.core.api.TemplateEngine;
import com.simafei.flow.core.exception.ApiCallException;
import com.simafei.flow.core.json.JsonUtils;
import groovy.lang.GroovyClassLoader;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

/**
 * @author fengpengju
 */
public class JsonApiAdapter implements ApiAdapter {

    private final HttpClient httpClient;

    private final TemplateEngine templateEngine;

    private final LoadingCache<Api, ApiResolver> groovyClassCache;

    private final GroovyClassLoader classLoader = new GroovyClassLoader();

    public JsonApiAdapter() {
        this(HttpClient.newHttpClient(), new TemplateEngineImpl());
    }

    public JsonApiAdapter(HttpClient httpClient) {
        this(httpClient, new TemplateEngineImpl());
    }

    public JsonApiAdapter(HttpClient httpClient, TemplateEngine templateEngine) {
        groovyClassCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .softValues()
                .recordStats()
                .build(api -> {
                    if (StrUtil.isEmpty(api.getSerializeScript())) {
                        return new DefaultApiResolver();
                    }
                    Class<?> groovyClass = classLoader.parseClass(api.getSerializeScript());
                    return (ApiResolver) groovyClass.getDeclaredConstructor().newInstance();
                });

        this.httpClient = httpClient;

        this.templateEngine = templateEngine;
    }

    @Override
    public List<Map<String, Object>> exchange(Api api, Map<String, Object> params) {
        ApiResolver apiResolver = groovyClassCache.get(api);
        if (apiResolver == null) {
            throw new ApiCallException("api resolver is null");
        }
        if (!apiResolver.shouldExecute(params)) {
            // 如果模型不需要执行，直接返回
            return List.of(params);
        }

        Map<String, String> headerMap = apiResolver.headers(params);

        // 如果是URL参数，用模版引擎渲染动态参数
        String url = templateEngine.render(api.getHttpUrl(), params);
        // 执行请求
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/json")
                .method(api.getHttpMethod(), HttpRequest.BodyPublishers.ofString(apiResolver.body(params)));
        if (CollectionUtil.isNotEmpty(headerMap)) {
            headerMap.forEach(builder::header);
        }

        try {
            HttpResponse<String> response = httpClient.send(
                    builder.build(), HttpResponse.BodyHandlers.ofString());

            int successCode = 200;
            // 返回结果
            if (response.statusCode() == successCode) {
                if (StrUtil.isNotEmpty(response.body())) {
                    Map<String, Object> result = JsonUtils.parseObject(response.body(), new TypeReference<>() {
                    });
                    return apiResolver.resolve(result, params);
                } else {
                    return List.of();
                }
            }
            throw new ApiCallException("api call failed");
        } catch (IOException | InterruptedException e) {
            throw new ApiCallException("call api error", e);
        }
    }

    @Override
    public SerializeType serializeType() {
        return SerializeType.JSON;
    }
}
